function D takes string msg returns nothing
	call BJDebugMsg(msg)
endfunction

function AngleNormalize takes real angle returns real
	local real answer = angle - (I2R((R2I(angle) / 360)) * 360.0)
	if answer < 0.0 then
		return answer + 360.0
	else
		return answer
	endif
endfunction

function WithinAngle takes real testingAngle , real centerAngle , real rangeAngle returns boolean
	local real halfRange = 0.0
	local real left = 0.0
	local real right = 0.0
	//--
	set testingAngle = AngleNormalize(testingAngle)
	set centerAngle = AngleNormalize(centerAngle)
	set rangeAngle = AngleNormalize(rangeAngle)
	set halfRange = rangeAngle * 0.5
	set left = centerAngle - halfRange
	set right = centerAngle + halfRange
	if left < 0.0 then
		set left = left + 360.0
		if testingAngle <= right or testingAngle >= left then
			return true
		else
			return false
		endif
	elseif right >= 360.0 then
		set right = right - 360.0
		if testingAngle >= left or testingAngle <= right then
			return true
		else
			return false
		endif
	else
		if testingAngle >= left and testingAngle <= right then
			return true
		else
			return false
		endif
	endif
endfunction

function ResetUnitAnimationScale takes nothing returns nothing
	local timer theTimer = GetExpiredTimer()
	local integer timerId = GetHandleId(theTimer)
	local unit unitToReset = LoadUnitHandle(udg_HT , timerId , 3)
	//--
	call PauseTimer(theTimer)
	call SetUnitTimeScale(unitToReset , 1.0)
	call FlushChildHashtable(udg_HT , timerId)
	call DestroyTimer(theTimer)
	call RemoveSavedHandle(udg_UnitDataHashTable , GetHandleId(unitToReset) , udg_AnimationTimer)
	//--
	set theTimer = null
	set unitToReset = null
endfunction

function UnitPlayAnimation takes unit whichUnit , integer animationId , real playSpeed, real duration returns nothing
	local integer unitId = GetHandleId(whichUnit)
	local timer resetScaleTimer = LoadTimerHandle(udg_UnitDataHashTable , unitId , udg_AnimationTimer)
	//--
	if resetScaleTimer == null then
		set resetScaleTimer = CreateTimer()
	else //已經有個timer在跑
		call PauseTimer(resetScaleTimer)	
	endif
	call SetUnitTimeScale(whichUnit , playSpeed)
	call SetUnitAnimationByIndex(whichUnit , animationId)
	call SaveUnitHandle(udg_HT , GetHandleId(resetScaleTimer) , 3 , whichUnit)
	call TimerStart(resetScaleTimer , duration , false , function ResetUnitAnimationScale)
endfunction

function AddGold takes player whichPlayer , integer goldValue returns nothing
	call SetPlayerState(whichPlayer , PLAYER_STATE_RESOURCE_GOLD , GetPlayerState(whichPlayer , PLAYER_STATE_RESOURCE_GOLD) + goldValue)
endfunction

function CreateTempTextTag takes string text , real size , real x , real y , real height , integer r , integer g , integer b , real xSpeed , real ySpeed , boolean isPermanent , real life , real fadePoint returns nothing
	local texttag tag = CreateTextTag()
	//--
	call SetTextTagText(tag , text , size)
	call SetTextTagPos(tag , x - (I2R(StringLength(text)) * size * 800.0) , y , height)
    call SetTextTagColor(tag , r , g , b , 255)
    call SetTextTagVelocity(tag , xSpeed , ySpeed)
    call SetTextTagPermanent(tag , isPermanent)
	if not isPermanent then
		call SetTextTagLifespan(tag , life)
		call SetTextTagFadepoint(tag , fadePoint)
	endif
	//--
	set tag = null
endfunction

function CreateTempChineseTextTag takes string text , real size , real x , real y , real height , integer r , integer g , integer b , real xSpeed , real ySpeed , boolean isPermanent , real life , real fadePoint returns nothing
	local texttag tag = CreateTextTag()
	//--
	call SetTextTagText(tag , text , size)
	call SetTextTagPos(tag , x - (I2R(StringLength(text)) * size * 400.0) , y , height)
    call SetTextTagColor(tag , r , g , b , 255)
    call SetTextTagVelocity(tag , xSpeed , ySpeed)
    call SetTextTagPermanent(tag , isPermanent)
	if not isPermanent then
		call SetTextTagLifespan(tag , life)
		call SetTextTagFadepoint(tag , fadePoint)
	endif
	//--
	set tag = null
endfunction

function CreateTextTagOnUnit takes string text , real size , unit onThisUnit , real height , integer r , integer g , integer b , real xSpeed , real ySpeed , boolean isPermanent , real life , real fadePoint returns nothing
	call CreateTempTextTag(text , size , GetUnitX(onThisUnit) , GetUnitY(onThisUnit) , height , r , g , b , xSpeed , ySpeed , isPermanent , life , fadePoint)
endfunction

function CreateChineseTextTagOnUnit takes string text , real size , unit onThisUnit , real height , integer r , integer g , integer b , real xSpeed , real ySpeed , boolean isPermanent , real life , real fadePoint returns nothing
	call CreateTempChineseTextTag(text , size , GetUnitX(onThisUnit) , GetUnitY(onThisUnit) , height , r , g , b , xSpeed , ySpeed , isPermanent , life , fadePoint)
endfunction

function AddGoldWithTextTag takes player whichPlayer , integer goldValue , unit textTagOnThisUnit returns nothing
	call AddGold(whichPlayer , goldValue)
	call CreateTextTagOnUnit("+" + I2S(goldValue) , 0.027 , textTagOnThisUnit , 50.0 , 255 , 255 , 0 , 0.0 , 0.03 , false , 1.0 , 0.5)
endfunction

function PlaySoundOnUnit takes unit who , string path returns nothing
	local sound snd = CreateSound(path , false , true , true , 0 , 0 , "DefaultEAXON")
	local location targetLocation = GetUnitLoc(who)
	call SetSoundChannel(snd , 0)
	call SetSoundDistances(snd , 600.0 , 10000.0)
	call SetSoundDistanceCutoff(snd , 3000.0)
	call SetSoundDuration(snd , GetSoundFileDuration(path))
	call SetSoundVolume(snd , 127 )
	call SetSoundConeAngles(snd , 0.0 , 0.0 , 127)
	call SetSoundConeOrientation(snd , 0.0 , 0.0 , 0.0)
	call SetSoundPitch(snd , 1.0)
	call SetSoundPosition(snd , GetLocationX(targetLocation) , GetLocationY(targetLocation) , 0.0)
	call StartSound(snd)
	call KillSoundWhenDone(snd)
	call RemoveLocation(targetLocation)
	set targetLocation = null
	set snd = null
endfunction

function KillUnitinSecondTimeUp takes nothing returns nothing
	local timer killerTimer = GetExpiredTimer()
	local integer timerId = GetHandleId(killerTimer)
	local unit unitToRemove = LoadUnitHandle(udg_HT , timerId , 3)
	//--
	call PauseTimer(killerTimer)
	call KillUnit(unitToRemove)
	call FlushChildHashtable(udg_HT , timerId)
	call DestroyTimer(killerTimer)
	//--
	set killerTimer = null
	set unitToRemove = null
endfunction

function KillUnitinSecond takes unit unitToRemove , real second returns nothing
	local timer killerTimer = CreateTimer()
	//--
	call SaveUnitHandle(udg_HT , GetHandleId(killerTimer) , 3 , unitToRemove)
	call TimerStart(killerTimer , second , false , function KillUnitinSecondTimeUp)
	//--
	set killerTimer = null
endfunction

function KillCrackinSecondTimeUp takes nothing returns nothing
	local timer killerTimer = GetExpiredTimer()
	local integer timerId = GetHandleId(killerTimer)
	local ubersplat crackToRemove = LoadUbersplatHandle(udg_HT , timerId , 3)
	//--
	call PauseTimer(killerTimer)
	call DestroyUbersplat(crackToRemove)
	call FlushChildHashtable(udg_HT , timerId)
	call DestroyTimer(killerTimer)
	//--
	set killerTimer = null
	set crackToRemove = null
endfunction

function KillCrackinSecond takes ubersplat theCrack , real secondToDestroy returns nothing
	local timer killerTimer = CreateTimer()
	//--
	call SaveUbersplatHandle(udg_HT , GetHandleId(killerTimer) , 3 , theCrack)
	call TimerStart(killerTimer , secondToDestroy , false , function KillCrackinSecondTimeUp)
	//--
	set killerTimer = null
endfunction

function CreateCrackTimed takes real x , real y , string modelName , real second returns nothing
	local ubersplat crack = CreateUbersplat(x , y , modelName , 255 , 255 , 255 , 255 , false , false)
	call SetUbersplatRenderAlways(crack , true)
	call KillCrackinSecond(crack , second)
	set crack = null
endfunction

function ReplaceConnectionBuilding takes nothing returns nothing
	local unit powerPlant = GetEnumUnit()
	local integer powerPlantId = GetHandleId(powerPlant)
	local unit oldUnit = udg_TempUnitToBeReplaced
	local integer oldUnitId = GetHandleId(oldUnit)
	local unit newUnit = udg_TempUnitReplacer
	local integer newUnitId = GetHandleId(newUnit)
	local hashtable buildingTable = LoadHashtableHandle(udg_UnitDataHashTable , powerPlantId , StringHash("SUPPLY_TABLE"))
	local group supplyBuildings = LoadGroupHandle(udg_UnitDataHashTable , powerPlantId , StringHash("SUPPLIED_BUILDINGS"))
	//--
	call GroupRemoveUnit(supplyBuildings , oldUnit)
	call GroupAddUnit(supplyBuildings , newUnit)
	call SaveInteger(buildingTable , newUnitId , StringHash("SUPPLY_USED") , LoadInteger(buildingTable , oldUnitId , StringHash("SUPPLY_USED")))
	call SaveLightningHandle(buildingTable , newUnitId , StringHash("POWER_SUPPLY_LIGHTNING") , LoadLightningHandle(buildingTable , oldUnitId , StringHash("POWER_SUPPLY_LIGHTNING")))
	call FlushChildHashtable(buildingTable , oldUnitId)
	//--
	set powerPlant = null
	set oldUnit = null
	set newUnit = null
	set buildingTable = null
endfunction

function ReplaceUnitSimpleDontSelect takes unit who , integer unitTypeId returns nothing
	local integer oldUnitId = GetHandleId(who)
	local unit newUnit = null
	local integer newUnitId = 0
	local real whoX = GetUnitX(who)
	local real whoY = GetUnitY(who)
	local integer whichSlot = 0
	local item whichItem = null
	local group usingPowerPlants = null
	//--
	if who == null or unitTypeId == 0 then
		return
	endif
	
	set newUnit = CreateUnit(GetOwningPlayer(who) , unitTypeId , whoX , whoY , GetUnitFacing(who))
	set newUnitId = GetHandleId(newUnit)
	
	if LoadBoolean(udg_UnitTypeDataHashTable , GetUnitTypeId(who) , StringHash("NEED_POWER_SUPPLY")) then
		call SaveBoolean(udg_UnitDataHashTable , oldUnitId , StringHash("IS_REPLACED") , true)
		set usingPowerPlants = LoadGroupHandle(udg_UnitDataHashTable , oldUnitId , StringHash("USING_POWER_PLANTS"))
		set udg_TempUnitToBeReplaced = who
		set udg_TempUnitReplacer = newUnit
		call ForGroup(usingPowerPlants , function ReplaceConnectionBuilding)		
		call SaveGroupHandle(udg_UnitDataHashTable , newUnitId , StringHash("USING_POWER_PLANTS") , usingPowerPlants)
		call SaveInteger(udg_UnitDataHashTable , newUnitId , StringHash("SUPPLY_NOW") , LoadInteger(udg_UnitDataHashTable , oldUnitId , StringHash("SUPPLY_NOW")))
		call SaveTextTagHandle(udg_UnitDataHashTable , newUnitId , StringHash("NEED_POWER_SUPPLY_BUILDING_TEXT_TAG") , LoadTextTagHandle(udg_UnitDataHashTable , oldUnitId , StringHash("NEED_POWER_SUPPLY_BUILDING_TEXT_TAG")))
		
		call SaveBoolean(udg_UnitDataHashTable , newUnitId , StringHash("CONSTRUCTION_FINISHED") , true)
		
	endif
	
	if GetUnitState(who , UNIT_STATE_MAX_LIFE) > 0 then
		call SetUnitState(newUnit , UNIT_STATE_LIFE , GetUnitState(newUnit , UNIT_STATE_MAX_LIFE) * GetUnitState(who , UNIT_STATE_LIFE) / GetUnitState(who , UNIT_STATE_MAX_LIFE))
	endif
	if GetUnitState(who , UNIT_STATE_MAX_MANA) > 0 then
		call SetUnitState(newUnit , UNIT_STATE_MANA , GetUnitState(newUnit , UNIT_STATE_MAX_MANA) * GetUnitState(who , UNIT_STATE_MANA) / GetUnitState(who , UNIT_STATE_MAX_MANA))
	endif
	
	set whichSlot = 0
	loop
	exitwhen whichSlot >= 6
		set whichItem = UnitItemInSlot(who , whichSlot)
		call UnitAddItem(newUnit , whichItem)
		call UnitDropItemSlot(newUnit , whichItem , whichSlot)
	set whichSlot = whichSlot + 1
	endloop
	
	if IsUnitPaused(who) then
		call PauseUnit(newUnit , true)
	endif
	
	set bj_lastCreatedUnit = newUnit
	set bj_lastReplacedUnit = newUnit
	
	call FlushChildHashtable(udg_UnitDataHashTable , oldUnitId)
	
	call KillUnit(who)
	call RemoveUnit(who)
		
	call SetUnitPosition(newUnit , whoX , whoY)
	//--
	set newUnit = null
	set whichItem = null
	set usingPowerPlants = null
endfunction

function ReplaceUnitSimple takes unit who , integer unitTypeId returns nothing
	call ReplaceUnitSimpleDontSelect(who , unitTypeId)	
	if IsUnitSelected(who , GetLocalPlayer()) then
		call SelectUnit(bj_lastReplacedUnit , true)
	endif
endfunction

function UnitAddMaxHp takes unit unitToAddMaxHp , real hp returns nothing
	local integer hpI = R2I(hp)
	local integer d = 0
	local integer i = 0
	//--
	
	if hp <= 0 then
		return
	endif
	
	if hpI >= 1000 then
		set d = hpI / 1000
		set i = 0
		loop
		exitwhen i >= d
			call UnitAddItemById(unitToAddMaxHp , 'I00K')
		set i = i + 1
		endloop
		set hpI = ModuloInteger(hpI , 1000)
	endif
	
	if hpI >= 100 then
		set d = hpI / 100
		set i = 0
		loop
		exitwhen i >= d
			call UnitAddItemById(unitToAddMaxHp , 'I00L')
		set i = i + 1
		endloop
		set hpI = ModuloInteger(hpI , 100)
	endif

	if hpI >= 10 then
		set d = hpI / 10
		set i = 0
		loop
		exitwhen i >= d
			call UnitAddItemById(unitToAddMaxHp , 'I00M')
		set i = i + 1
		endloop
		set hpI = ModuloInteger(hpI , 10)
	endif

	if hpI >= 1 then
		set d = hpI
		set i = 0
		loop
		exitwhen i >= d
			call UnitAddItemById(unitToAddMaxHp , 'I00N')
		set i = i + 1
		endloop
	endif
endfunction

function TriggerRegisterPlayerForceUnitEvent takes trigger whichTrigger , playerunitevent whichEvent returns nothing
    local integer index = 0
    loop
    	if GetPlayerController(Player(index)) == MAP_CONTROL_USER then
        	call TriggerRegisterPlayerUnitEvent(whichTrigger , Player(index), whichEvent , null)
        endif
        set index = index + 1
        exitwhen index >= 10
    endloop
endfunction

function TriggerRegisterPlayerForceChatEvent takes trigger whichTrigger , string chatMessage returns nothing
    local integer index = 0
    loop
    	if GetPlayerController(Player(index)) == MAP_CONTROL_USER then
        	call TriggerRegisterPlayerChatEvent (whichTrigger , Player(index) , chatMessage , true)
        endif
        set index = index + 1
        exitwhen index >= 10
    endloop
endfunction

function GetRandomRealTriangleDistribution takes real min , real max returns real
	local real randomNumber = GetRandomReal(0.0 , 1.0)
	if randomNumber < 0.5 then
		return min + SquareRoot(0.5 * randomNumber) * (max - min)
	else
		return min + (1.0 - SquareRoot(0.5 - 0.5 * randomNumber)) * (max - min)
	endif
endfunction

function GetRandomExponential takes nothing returns real
	local real randomNumber = GetRandomReal(0.0 , 1.0)
	return randomNumber * randomNumber
endfunction

function ModifyUnitDefendPercent takes unit whichUnit , real percentModifier returns nothing
	call SaveReal(udg_UnitDataHashTable , GetHandleId(whichUnit) , StringHash("UNIT_DEFEND_PERCENT") , LoadReal(udg_UnitDataHashTable , GetHandleId(whichUnit) , StringHash("UNIT_DEFEND_PERCENT")) + percentModifier)
endfunction

function For takes group whichGroup , code callback returns nothing
	call ForGroup(whichGroup , callback)
	call DestroyGroup(whichGroup)
endfunction

function GetBattery takes unit whichUnit returns item
	local integer whichSlot = 0
	local item udg_TempItem = null
	//--
	loop
		set udg_TempItem = UnitItemInSlot(whichUnit , whichSlot)
		if LoadInteger(udg_ItemTypeDataHashTable , GetItemTypeId(udg_TempItem) , StringHash("ITEM_CATEGORY")) == StringHash("BATTERY") and GetItemCharges(udg_TempItem) > 0 then
			return udg_TempItem
		endif
	set whichSlot = whichSlot + 1
	exitwhen whichSlot > 5
	endloop
	return null
endfunction

function GetWeaponOfType takes unit whichUnit , integer whichWeaponType returns item
	local integer whichSlot = 0
	local item udg_TempItem = null
	//--
	loop
		set udg_TempItem = UnitItemInSlot(whichUnit , whichSlot)
		if LoadInteger(udg_ItemTypeDataHashTable , GetItemTypeId(udg_TempItem) , StringHash("WEAPON_TYPE")) == whichWeaponType then
			return udg_TempItem
		endif
	set whichSlot = whichSlot + 1
	exitwhen whichSlot > 5
	endloop
	return null
endfunction

function PrintP takes player whichPlayer , string message returns nothing
	if GetLocalPlayer() == whichPlayer then
		call DisplayTextToPlayer(whichPlayer , 0.0 , 0.0 , message)
	endif
endfunction

function CreateDinoControler takes unit dino , integer dinoType returns nothing
	local integer dinoId = GetHandleId(dino)
	local trigger controlTrigger = CreateTrigger()
	local boolexpr controlCode = LoadBooleanExprHandle(udg_DinoTypeDataHashTable , dinoType , StringHash("DINO_CONTROL_CODE"))
	local triggercondition controlTriggerCondition = null
	//--
	call TriggerRegisterTimerEvent(controlTrigger , GetRandomReal(10.0 , 20.0) , true)
	set controlTriggerCondition = TriggerAddCondition(controlTrigger , controlCode)
	call SaveTriggerHandle(udg_UnitDataHashTable , dinoId , StringHash("DINO_CONTROL_TRIGGER") , controlTrigger)
	call SaveTriggerConditionHandle(udg_UnitDataHashTable , dinoId , StringHash("DINO_CONTROL_TRIGGER_CONDITION") , controlTriggerCondition)
	//--
	set controlTrigger = null
	set controlCode = null
	set controlTriggerCondition = null
endfunction

function DinoSetStrength takes unit dino returns nothing
	local integer dinoType = GetUnitTypeId(dino)
	local real dinoHpBounsMean = LoadReal(udg_DinoTypeDataHashTable , dinoType , StringHash("HP_BONUS_MEAN"))
	local real dinoOriginalScale = LoadReal(udg_DinoTypeDataHashTable , dinoType , StringHash("ORIGINAL_SCALE"))
	local real dinoScaleUpCoefficient = LoadReal(udg_DinoTypeDataHashTable , dinoType , StringHash("SCALE_UP_COEFFICIENT"))
	local real dinoHpBonus = 0.0
	local real scale = dinoOriginalScale
	local hashtable abilityList = LoadHashtableHandle(udg_DinoTypeDataHashTable , dinoType , StringHash("ABILITY_LIST"))
	local integer amountOfAbilities = LoadInteger(abilityList , 0 , StringHash("LIST_SIZE"))
	local integer whichAbility = 0
	//--
	set dinoHpBonus = GetRandomRealTriangleDistribution(dinoHpBounsMean - udg_ElapsedGameTime * 0.3 , dinoHpBounsMean + udg_ElapsedGameTime * 0.3)
	set scale = dinoOriginalScale * (1.0 + dinoHpBonus / dinoScaleUpCoefficient)
	call SetUnitScale(dino , scale , scale , scale)
	call UnitAddMaxHp(dino , dinoHpBonus)
	loop
		exitwhen whichAbility >= amountOfAbilities
		if GetRandomReal(0.0 , 1.0) <= udg_ElapsedGameTime / LoadReal(abilityList , whichAbility , StringHash("PROBABILITY_COEFFICIENT")) then
			call UnitAddAbility(dino , LoadInteger(abilityList , whichAbility , StringHash("ABILITY")))
		endif
		set whichAbility = whichAbility + 1
	endloop
	//--
	set abilityList = null
endfunction

function CreateDinoGeneral takes integer dinoType , location spawnLocation returns nothing
	local unit dino = null
	//--
	if spawnLocation == null then
		set spawnLocation = GetRandomLocInRect(udg_CreateDinoRects[GetRandomInt(0 , 3)])
	endif
	set dino = CreateUnit(Player(11) , dinoType , GetLocationX(spawnLocation) , GetLocationY(spawnLocation) , GetRandomReal(0.0 , 360.0))
	call RemoveLocation(spawnLocation)
	call DinoSetStrength(dino)
	call CreateDinoControler(dino , dinoType)
	set spawnLocation = GetRandomLocInRect(GetPlayableMapRect())
	call IssuePointOrderLoc(dino , "attack" , spawnLocation)
	call RemoveLocation(spawnLocation)
	set udg_AmountOfAliveDinos = udg_AmountOfAliveDinos + 1
	call LeaderboardSetPlayerItemValueBJ(Player(11) , GetLastCreatedLeaderboard() , udg_AmountOfAliveDinos)
	//--
	set dino = null
	set spawnLocation = null
endfunction

function GetRandomDinoType takes nothing returns integer
	local real trexHp = 1.0 / (LoadReal(udg_DinoTypeDataHashTable , 'o002' , StringHash("MIN_HP")) + LoadReal(udg_DinoTypeDataHashTable , 'o002' , StringHash("HP_BONUS_MEAN")))
	local real allosaurusHp = 1.0 / (LoadReal(udg_DinoTypeDataHashTable , 'o000' , StringHash("MIN_HP")) + LoadReal(udg_DinoTypeDataHashTable , 'o000' , StringHash("HP_BONUS_MEAN")))
	local real spinosaurusHp = 1.0 / (LoadReal(udg_DinoTypeDataHashTable , 'o003' , StringHash("MIN_HP")) + LoadReal(udg_DinoTypeDataHashTable , 'o003' , StringHash("HP_BONUS_MEAN")))
	local real velHp = 1.0 / (LoadReal(udg_DinoTypeDataHashTable , 'o001' , StringHash("MIN_HP")) + LoadReal(udg_DinoTypeDataHashTable , 'o001' , StringHash("HP_BONUS_MEAN")))
	local real dilophosaurusHp = 1.0 / (LoadReal(udg_DinoTypeDataHashTable , 'o004' , StringHash("MIN_HP")) + LoadReal(udg_DinoTypeDataHashTable , 'o004' , StringHash("HP_BONUS_MEAN")))
	local real sum = trexHp + allosaurusHp + spinosaurusHp + velHp + dilophosaurusHp
	local real ratio = 1.0 / sum
	local real randomNumber = GetRandomReal(0.0 , 1.0)
	local real sumNow = 0.0
	local real time = udg_ElapsedGameTime
	//--
	call SaveReal(udg_DinoTypeDataHashTable , 'o002' , StringHash("HP_BONUS_MEAN") , time * 3.3333)
	call SaveReal(udg_DinoTypeDataHashTable , 'o000' , StringHash("HP_BONUS_MEAN") , time * 2.9166)
	call SaveReal(udg_DinoTypeDataHashTable , 'o003' , StringHash("HP_BONUS_MEAN") , time * 1.9166)
	call SaveReal(udg_DinoTypeDataHashTable , 'o001' , StringHash("HP_BONUS_MEAN") , time * 0.75)
	call SaveReal(udg_DinoTypeDataHashTable , 'o004' , StringHash("HP_BONUS_MEAN") , time * 0.625)
	
	set sumNow = sumNow + trexHp * ratio
	if randomNumber < sumNow then
		return 'o002'
	endif
	set sumNow = sumNow + allosaurusHp * ratio
	if  randomNumber < sumNow then
		return 'o000'
	endif
	set sumNow = sumNow + spinosaurusHp * ratio
	if  randomNumber < sumNow then
		return 'o003'
	endif
	set sumNow = sumNow + velHp * ratio
	if  randomNumber < sumNow then
		return 'o001'
	endif
	set sumNow = sumNow + dilophosaurusHp * ratio
	if  randomNumber < sumNow then
		return 'o004'
	endif
endfunction

function GetDefaultAbility takes integer hotKey returns integer
	if hotKey == StringHash("Q") then
		return 'A009'
	elseif hotKey == StringHash("W") then
		return 'A00B'
	elseif hotKey == StringHash("E") then
		return 'A00A'
	endif
	return 0
endfunction

function FakeHotkeyToRealHotkey takes integer hotkey returns string
	if hotkey == StringHash("Q") then
		return "q"
	elseif hotkey == StringHash("W") then
		return "w"
	elseif hotkey == StringHash("E") then
		return "e"
	elseif hotkey == StringHash("R") then
		return "r"
	endif
endfunction

function UnitUsedAbilityWithDelta takes unit caster , integer abilityId , real delta returns nothing
	local integer abilityMasteryId = LoadInteger(udg_AbilityDataHashTable , abilityId , StringHash("MASTERY_ID"))
	local real mastery = LoadReal(udg_UnitDataHashTable , GetHandleId(caster) , abilityMasteryId)
	local real newMastery = mastery + delta
	local integer abilityLevelNow = GetUnitAbilityLevel(caster , abilityId)
	local hashtable masteryTable = LoadHashtableHandle(udg_AbilityDataHashTable , abilityId , StringHash("LEVEL_MASTERY_TABLE"))
	local integer maxLevel = LoadInteger(udg_AbilityDataHashTable , abilityId , StringHash("MAX_LEVEL"))
	local boolean levelUp = false
	local integer nextAbility = LoadInteger(udg_AbilityDataHashTable , abilityId , StringHash("NEXT_ABILITY"))
	local player owner = GetOwningPlayer(caster)
	local hashtable casterAvailableAbilityTable = LoadHashtableHandle(udg_PlayerDataHashTable , GetHandleId(owner) , StringHash("ABILITY_AVAILABLE_TABLE"))
	//--
	call SaveReal(udg_UnitDataHashTable , GetHandleId(caster) , abilityMasteryId , newMastery)
	loop
		if abilityLevelNow < maxLevel then
			if newMastery >= LoadReal(masteryTable , abilityLevelNow , 0) then
				set abilityLevelNow = abilityLevelNow + 1
				set levelUp = true
			else
				exitwhen true
			endif
		else
			exitwhen true
		endif
	endloop
	
	if levelUp then
		call SetUnitAbilityLevel(caster , abilityId , abilityLevelNow)
		call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\AIlm\\AIlmTarget.mdl" , caster , "origin"))
	endif
	
	if HaveSavedInteger(udg_AbilityDataHashTable , abilityId , StringHash("NEXT_ABILITY")) and LoadBoolean(casterAvailableAbilityTable , nextAbility , StringHash("LEARNED")) == false and newMastery >= LoadReal(masteryTable , maxLevel , 0) then
		call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Levelup\\LevelupCaster.mdl" , caster , "origin"))
		call SaveBoolean(casterAvailableAbilityTable , nextAbility , StringHash("LEARNED") , true)
		call SetPlayerAbilityAvailable(owner , nextAbility , true)
		call SetPlayerAbilityAvailable(owner , GetDefaultAbility(LoadInteger(udg_AbilityDataHashTable , nextAbility , StringHash("HOTKEY"))) , false)
		call PrintP(owner , "你學會了" + LoadStr(udg_AbilityDataHashTable , nextAbility , StringHash("NAME")) + "!")
		call PrintP(owner , "輸入 -s" + FakeHotkeyToRealHotkey(LoadInteger(udg_AbilityDataHashTable , nextAbility , StringHash("HOTKEY"))) + " 可以在武器技能與初始技能間切換")
	endif
	//--
	set masteryTable = null
	set owner = null
	set casterAvailableAbilityTable = null
endfunction